using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.Win32;
using iTunesLib;

namespace TunesHelper
{
  public partial class MainForm : Form
  {
    private iTunesApp _itunes;
    private List<string> _pauseOnProcs;
    private bool _isOnPause;
    private ManagementEventWatcher _startWatch ;
    private ManagementEventWatcher _stopWatch;
    private KeyboardHook _keyboardHook;
    private Options _options;
    private const string c_file = "iTunesHelper.xml";

    public MainForm()
    {
      InitializeComponent();
      _pauseOnProcs = new List<string>();
      SizeChanged += delegate { if (FormWindowState.Minimized == WindowState) Hide(); };
      _options = new Options();
    }

    private void OnFormLoad(object sender, EventArgs e)
    {
      Text += string.Format(" {0}.{1}.{2}", GetProductVersion().Major, GetProductVersion().Minor, GetProductVersion().Build);
      try
      {
        SystemEvents.SessionSwitch += SystemEventsOnSessionSwitch;
        InitProcessWatchers();
        
        LoadOptions();
        InitHotkeys();
        _lockPauseChk.Checked = _options.PauseOnLock;
        _prcPauseChk.Checked = _options.PauseOnProcess;
        _pauseOnProcs = _options.Processes;
        _prcTextBox.Text = string.Join("; ", _pauseOnProcs.ToArray());

        _itunes = new iTunesApp();
      }
      catch (Exception ex)
      {
        MessageBox.Show(string.Format("Failed to init iTunes lib.\r\n{0}", ex.Message));
      }
    }

    public static Version GetProductVersion()
    {
      return Assembly.GetCallingAssembly().GetName().Version;
    }

    private void OnFormClosing(object sender, FormClosingEventArgs e)
    {
      SystemEvents.SessionSwitch -= SystemEventsOnSessionSwitch;
      _startWatch.Stop();
      _startWatch.Dispose();
      _stopWatch.Stop();
      _stopWatch.Dispose();

      _options.Processes.Clear();
      var procs = _prcTextBox.Text.Split(';');
      if (procs.Length > 0)
        foreach (var item in procs.Where(item => !string.IsNullOrEmpty(item)))
          _options.Processes.Add(item.Trim());
      _options.PauseOnLock = _lockPauseChk.Checked;
      _options.PauseOnProcess = _prcPauseChk.Checked;
      SaveOptions();
    }

    private void InitHotkeys()
    {
      _keyboardHook = new KeyboardHook();
      _keyboardHook.KeyPressed += KeyboardHookOnKeyPressed;
      foreach (iTunesAction item in Enum.GetValues(typeof(iTunesAction)))
      {
        var fromOptions = _options.HotKeys.FirstOrDefault(hk => hk.Action == item);
        if (fromOptions != null) continue;
        var row = new HotkeyRow { Action = item, Hotkey = new Hotkey() };
        _options.HotKeys.Add(row);
      }
      foreach (var hotKey in _options.HotKeys)
        RegisterHotKey(hotKey);
      _hkGrid.DataSource = _options.HotKeys;
    }

    private void RegisterHotKey(HotkeyRow row)
    {
      _keyboardHook.RegisterHotKey(row.Action, row.Hotkey);
    }

    private void KeyboardHookOnKeyPressed(object sender, iTunesAction e)
    {
      var option = _options.HotKeys.FirstOrDefault(hk => hk.Action == e);
      if (option == null || option.IsActive == false) return;
      switch (e)
      {
        case iTunesAction.Previous:
          _itunes.PreviousTrack();
          break;
        case iTunesAction.Next:
          _itunes.NextTrack();
          break;
        case iTunesAction.PlayOrPause:
          _itunes.PlayPause();
          break;
        case iTunesAction.VolumeDown:
          _itunes.SoundVolume -= 5;
          break;
        case iTunesAction.VolumeUp:
          _itunes.SoundVolume += 5;
          break;
        default:
          throw new ArgumentOutOfRangeException("e");
      }
    }

    #region Processes handling

    private void InitProcessWatchers()
    {
      _startWatch = new ManagementEventWatcher(
        new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
      _startWatch.EventArrived += ProcessStarted;
      _startWatch.Start();
      _stopWatch = new ManagementEventWatcher(
        new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace"));
      _stopWatch.EventArrived += ProcessStopped;
      _stopWatch.Start();
    }

    private void ProcessStopped(object sender, EventArrivedEventArgs e)
    {
      if (!_prcPauseChk.Checked) return;
      UpdateRunningProcs();
    }

    private void ProcessStarted(object sender, EventArrivedEventArgs e)
    {
      if (!_prcPauseChk.Checked) return;
      UpdateRunningProcs();
    }

    private void UpdateRunningProcs()
    {
      var winProcs = Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToList();
      var filtered = winProcs.Where(Predicate).ToList();
      if (filtered.Count > 0)
      {
        if (_itunes.PlayerState == ITPlayerState.ITPlayerStatePlaying)
        {
          _itunes.Pause();
          _isOnPause = true;
        }
      }
      else if (_isOnPause)
      {
        _itunes.Play();
        _isOnPause = false;
      }
    }

    private bool Predicate(Process p)
    {
      var moduleName = p.MainModule.ModuleName.ToLowerInvariant();
      var processName = p.ProcessName.ToLowerInvariant();
      return _pauseOnProcs.Any(pp => pp == moduleName || pp == processName);
    }

    #endregion

    private void SystemEventsOnSessionSwitch(object sender, SessionSwitchEventArgs e)
    {
      if (!_lockPauseChk.Checked) return;
      switch (e.Reason)
      {
        case SessionSwitchReason.SessionLogoff:
        case SessionSwitchReason.SessionLock:
          if (_itunes != null) _itunes.Pause();
          break;
        case SessionSwitchReason.SessionUnlock:
          if (_itunes != null) _itunes.PlayPause();
          break;
      }
    }

    private void MenuExit_Click(object sender, EventArgs e)
    {
      Close();
    }

    private void NotifyIcon_Click(object sender, MouseEventArgs e)
    {
      if (e.Button == MouseButtons.Left)
      {
        Show();
        WindowState = FormWindowState.Normal;
        BringToFront();
      }
    }

    private void OptionsShow_Click(object sender, EventArgs e)
    {
      Show();
      WindowState = FormWindowState.Normal;
      BringToFront();
    }

    private void SaveOptions()
    {
      try
      {
        Xml.Serialize(c_file, _options);
      }
      catch (Exception e)
      {
        MessageBox.Show(e.Message);
      }
    }

    private void LoadOptions()
    {
      try
      {
        _options = Xml.Deserialize<Options>(c_file);
      }
      catch
      {
        _options.PauseOnLock = true;
        _options.PauseOnProcess = true;
        _options.Processes = new List<string> { "gom", "kmplayer" };
      }
    }

    private void HotkeysGridCellContentClick(object sender, DataGridViewCellEventArgs e)
    {
      if (_hkGrid.Columns[e.ColumnIndex] != hotkeyDataGridViewTextBoxColumn) return;
      var row = _hkGrid.Rows[e.RowIndex].DataBoundItem as HotkeyRow;
      if (row == null) return;
      using (var hkd = new HotKeyDialog(row.Hotkey))
      {
        var res = hkd.ShowDialog(this);
        if (res != DialogResult.OK) return;
        row.Hotkey = hkd.GetHotkey();
        RegisterHotKey(row);
      }
    }

    private void HotkeysGridCurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
      if (_hkGrid.CurrentCell is DataGridViewCheckBoxCell)
        _hkGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }

  }
}